import Cocoa

//var str = "Hello, playground"

//泛型函数可以用于任何类型.
func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
    let tempValue = a
    a = b
    b = tempValue
}

/**
 类型形式参数:
 1. 类型形式参数指定并且命名一个占位符类型,紧挨着写在函数名后面的一对尖括号里(比如<T>)
 2. 一旦你指定了一个类型形式参数,你就可以用它定义一个函数形式参数,比如 swapTwoValues(_:_:) 函数中的形式参数 a 和 b )的类型, 或者用它做函数返回值类型, 或者做函数体重类型标注. 在不同情况下，用调用函数时的实际类型来替换类型形式参数。
 3. 你可以通过在尖括号里写多个用逗号隔开的类型形式参数名, 来提供更多类型形式参数.
 */
 

/**
 泛型类型:
 1. 除了泛型函数,Swift允许你定义自己的泛型类型. 他们是可以用于任意类型的自定义类,结构体,枚举, 和Array,Dictionary方式类似
 */

//IntStack
struct IntStack {
    var items = [Int]()
    mutating func push(_ item: Int) {
        items.append(item)
    }
    mutating func pop() -> Int {
        return items.removeLast()
    }
}

//泛型stack
struct Stack<Element> {
    var items = [Element]()
    mutating func push(_ item: Element) {
        items.append(item)
    }
    mutating func pop() -> Element {
        return items.removeLast()
    }
}
var stackOfString = Stack<String>()
stackOfString.push("a")
stackOfString.push("b")
stackOfString.push("c")
stackOfString.push("d")


/**
 扩展泛型类型:
 当你扩展泛型类型时, 不需要在扩展的定义中提供类型形式参数列表. 原始类型定义的类型形式参数列表在扩展体里仍然有效, 并且原始类型形式参数列表名称也用于扩展类型形式参
 数。
 */
extension Stack {
    var topItem: Element? {
        return items.isEmpty ? nil : items[items.count - 1]
    }
}

/**
 类型约束的语法:
 在一个类型形式参数名称后面放置一个类或者协议作为形式参数列表的一部分,并用冒号隔开,以写出一个类型约束.
 */

//类型约束的应用:
//这里可能查找数组中的字符串,也可能查找数组中的数字,所以可以使用泛型来表示这个函数
//func findFirst(ofString valueToFind: String, in array: [String]) -> Int? {
//    for (index, value) in array.enumerated() {
//        if value == valueToFind {
//            return index
//        }
//    }
//    return nil
//}
//print(findFirst(ofString: "lisi", in: ["zhangsan", "lisi", "wangwu"]))


//Swift 标准库中定义了一个叫做 Equatable 的协议，要求遵循其协议的类型要实现相等操作符( == )和不 等操作符( != )，用于比较该类型的任意两个值。所有Swift标准库中的类型自动支持 Equatable 协议。
func findFirst<T: Equatable>(of valueToFind: T, in array: [T]) -> Int? {
    for (index, value) in array.enumerated() {
        if value == valueToFind {
            return index
        }
    }
    return nil
}

/**
 关联类型:
 定义一个协议时, 有时在协议定义里声明一个或多个关联类型是很有用的. 关联类型给协议中用到的类型一个占位符名称. 知道采纳协议时, 才指定用于该关联类型的实际类型. 关联类型通过associatedType关键字指定
 */
protocol Container {
    associatedtype ItemType
    mutating func append(_ item: ItemType)
    var count: Int { get }
    subscript(i: Int) -> ItemType { get }
}

//关联类型的应用
struct IntStack2: Container {
    var items = [Int]()
    mutating func push(_ item: Int) {
        items.append(item)
    }
    mutating func pop() -> Int {
    return items.removeLast()
    }
    typealias ItemType = Int
    mutating func append(_ item: Int) {
        self.push(item)
    }
    var count: Int {
        return items.count
    }
    subscript(i: Int) -> Int {
        return items[i]
    }
}
var intStack = IntStack2()
intStack.append(1)
intStack.append(2)


//将具体类型转换成泛型
struct Stack2<Elment>: Container {
    var items = [Elment]()
    mutating func push(_ item: Elment) {
        items.append(item)
    }
    mutating func pop() -> Elment {
        return items.removeLast()
    }
    //这里可以不需要写typealias ItemType = Int来指明关联类型的实际类型, 因为Swift的类型推断很厉害
    mutating func append(_ item: Elment) {
        self.push(item)
    }
    var count: Int{
      return items.count
    }
    subscript(i: Int) -> Elment {
        return items[i]
    }
}


//你可以在协议里给关联类型添加约束来要求遵循的类型满足约束。
protocol Container2 {
    associatedtype Item: Equatable
    mutating func append(_ item: Item)
    var count: Int { get }
    subscript(i: Int) -> Item { get }
}


//协议可以作为它自身的要求出现。
protocol SuffixableContianer: Container2 {
    associatedtype Suffix: SuffixableContianer where Suffix.Item == Item
    func suffix(_ size: Int) -> Suffix
}


//where子句
//如类型约束中描述的一样，类型约束允许你在泛型函数或泛型类型相关的类型形式参数上定义要求。
func allItemsMatch<C1: Container, C2: Container>(container: C1, anotherContainer: C2) -> Bool where C1.ItemType == C2.ItemType, C1.ItemType: Equatable {
    if container.count != anotherContainer.count {
        return false
    }
    for i in 0..<container.count {
        if container[i] != anotherContainer[i] {
            return false
        }
    }
    return true
}


//带有泛型where子句的扩展
//你同时也可以使用泛型的 where 子句来作为扩展的一部分。
extension Stack where Element: Equatable {
    func isTop(_ item: Element) -> Bool {
        guard let topItem = items.last else {
            return false
        }
        return topItem == item
    }
}

struct NotEquatable { }
var notEquatableStack = Stack<NotEquatable>()
let notEquatableValue = NotEquatable()
notEquatableStack.push(notEquatableValue)
//notEquatableStack.isTop(notEquatableValue)  //这里报错,因为notEquatableValue不会遵循NotEquatable协议

extension Container where ItemType: Equatable {
    func startsWith(_ item: ItemType) -> Bool {
        return count >= 1 && self[0] == item
    }
}

extension Container where ItemType == Double {
    func average() -> Double {
        var sum = 0.0
        for index in 0..<count {
            sum += self[index]
        }
        return sum / Double(count)
    }
}

//你可以在关联类型中包含一个泛型where子句.
//protocol Container3 {
//    associatedtype Item
//    mutating func append(_ item: Item)
//    var count: Int { get }
//    subscript(i: Int) -> Item { get }
//
//    associatedtype Iterator: InteratorProtocol where Iterator.Element == Item
//    func makeIterator() -> Iterator
//
//}


/**
 泛型下标:
 下标可以是泛型, 他们可以包含泛型where子句. 你可以在subscript后用尖括号来写类型占位符, 你还可以在下标代码花括号前写泛型where分句.
 */
extension Container {
    subscript<Indices: Sequence>(indices: Indices) -> [ItemType] where Indices.Iterator.Element == Int {
        var result = [ItemType]()
        for index in indices {
            result.append(self[index])
        }
        return result
    }
}
print(intStack[[0, 1]])
